home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / More Source / C⁄C++ / CheeseToast 1.0.1 / Source / GamePlay.c < prev    next >
Text File  |  1994-03-10  |  20KB  |  902 lines

  1. /****************************************************************************
  2.  * GamePlay.c
  3.  *
  4.  *        Overall Game routines
  5.  *
  6.  * 2/7/94
  7.  *        Updated to work on 12 inch (512x384) monitors and 
  8.  *        to center properly on monitors larger than 640 x 480
  9.  *
  10.  * 4/28/93 - 5/93
  11.  *        Original Code Written
  12.  ****************************************************************************/
  13.  
  14. #define SPRITEMAIN    1
  15. #include "CToast.h"
  16. #include "math.h"
  17.  
  18. // Game Initialization
  19. //
  20. void InitializeGame(WindowPtr theWin)
  21. {
  22.     short            i,j,n,b;
  23.     GDHandle        gDevice;
  24.     Ptr                sp,dp,cp;
  25.     Handle            h;
  26.     OSErr            oe;
  27.     GDHandle        curDevice;
  28.  
  29.     // Fade out the screen (comment this out if you want to use debugger)
  30.     GammaFadeOut();
  31.     curDevice = GetGDevice();
  32.  
  33.     // Get Video Ptr and Dimensions
  34.     gVideoRowBytes = (*(*curDevice)->gdPMap)->rowBytes & 0x3FFF;
  35.     gVideoMem = (*(*curDevice)->gdPMap)->baseAddr;
  36.  
  37.     // Choose between 512x384 and 640x480 modes
  38.     //
  39.     if (screenBits.bounds.bottom - screenBits.bounds.top < 480 ||
  40.         screenBits.bounds.right - screenBits.bounds.left < 640) 
  41.     {
  42.         g12InchMode = true;
  43.         SetRect(&gOffscreenRect,0,0,512+IconWidth*2,384+IconWidth*2);
  44.         gVidOffset.h = ((screenBits.bounds.right - screenBits.bounds.left) - 512)/2;
  45.         gVidOffset.v = ((screenBits.bounds.bottom - screenBits.bounds.top) - 384)/2;
  46.     }
  47.     else {
  48.         SetRect(&gOffscreenRect,0,0,640+IconWidth*2,480+IconWidth*2);
  49.         gVidOffset.h = ((screenBits.bounds.right - screenBits.bounds.left) - 640)/2;
  50.         gVidOffset.v = ((screenBits.bounds.bottom - screenBits.bounds.top) - 480)/2;
  51.     }
  52.     
  53.     // Calc the center point
  54.     gCenterP.h = (gOffscreenRect.right - gOffscreenRect.left) / 2;
  55.     gCenterP.v = (gOffscreenRect.bottom - gOffscreenRect.top) / 2;
  56.  
  57.     // Allocate an offscreen pixel map
  58.     //
  59.     if ((oe = NewGWorld(&gOffScreen,0,&gOffscreenRect,NULL,NULL,0)) != noErr) {
  60.         DebugStr("\pOy!");
  61.         return;
  62.     }
  63.     gPixMap = GetGWorldPixMap(gOffScreen);
  64.     LockPixels(gPixMap);
  65.  
  66.     gScreenRowBytes = (*gPixMap)->rowBytes & 0x3FFF;
  67.     gScreenMem = (*gPixMap)->baseAddr;
  68.  
  69.     gPlayRect = gOffscreenRect;
  70.     gPlayRect.right -= IconWidth;
  71.     gPlayRect.bottom -= IconHeight;
  72.  
  73.     // Load User Preferences - Sound Volume, Keys
  74.     LoadPreferences();    // Do this before loading sounds - has volume setting
  75.  
  76.     // Load the Sounds
  77.     InitSounds();
  78.  
  79.     // Hide Menubar
  80.     oldMBarHeight = MBarHeight;
  81.     MBarHeight = 0;
  82.     RectRgn(theWin->visRgn,&screenBits.bounds);
  83.  
  84.     // Paint Startup Screen, Fade In
  85.     PaintRect(&theWin->portRect);
  86.     StartupScreen();
  87.     GammaFadeIn();
  88.  
  89.     // Draw Status Bar - used to show initialization progress
  90.     DrawStatusBar(0,100);
  91.  
  92.     // Randomize the Random Number Generator
  93.     GetDateTime((unsigned long *) &randSeed);
  94.  
  95.     // Play the startup sound - note: i'm getting a sound glitch
  96.     // when sound manager 3.0 is used
  97.     PlaySound(S_Startup, 4);
  98.  
  99.     // Calc dimensions of play field
  100.     gPlayWidth = gPlayRect.right - gPlayRect.left;
  101.     gPlayHeight = gPlayRect.bottom - gPlayRect.top;
  102.  
  103.     // Allocate tables
  104.     smTable = (SaveMapRecord *) NewPtrClear(sizeof(SaveMapRecord) * MaxSaveMaps);
  105.     if (smTable == NULL)
  106.         DebugStr("\pOut of Mem");
  107.  
  108.     sTable = (SpriteInstance *) NewPtrClear(sizeof(SpriteInstance) * MaxSprites);
  109.     if (sTable == NULL)
  110.         DebugStr("\pOut of Mem");
  111.  
  112.     // Load in Sprites
  113.     //
  114.     for (i = 0; i < NbrSprites; ++i) {
  115.         DrawStatusBar(i,NbrSprites+1);
  116.  
  117.         if (sDef[i].firstIconID) {
  118.             // Allocate Pix Maps
  119.             sDef[i].colorMaps = NewPtrClear(ColorMapSize * sDef[i].nbrIcons);
  120.             if (sDef[i].colorMaps == NULL)
  121.                 DebugStr("\pOut of Mem");
  122.             sDef[i].maskMaps = NewPtrClear(ColorMapSize *  sDef[i].nbrIcons);
  123.             if (sDef[i].maskMaps == NULL)
  124.                 DebugStr("\pOut of Mem");
  125.     
  126.             // Load Icons
  127.             for (j = 0; j < sDef[i].nbrIcons; ++j) {
  128.     
  129.                 // Get Color Sprite Picture
  130.                 h = GetResource('icl8',sDef[i].firstIconID+j);
  131.                 BlockMove(*h,sDef[i].colorMaps + AniFrameIndex(j), ColorMapSize);
  132.                 ReleaseResource(h);
  133.     
  134.                 // Get Mask, convert to 8 bit, clear unmasked colors in
  135.                 // sprite
  136.                 h = GetResource('ICN#',sDef[i].firstIconID+j);
  137.                 sp = *h + MaskMapSize;
  138.                 dp = sDef[i].maskMaps + AniFrameIndex(j);
  139.                 cp = sDef[i].colorMaps + AniFrameIndex(j);
  140.                 b = 0x80;
  141.                 for (n = 0; n < ColorMapSize; ++n) {
  142.                     if ((*sp & b) > 0) {
  143.                         *dp = 0;
  144.                     }
  145.                     else {
  146.                         *dp = 0xFF;
  147.                         *cp = 0;
  148.                     }
  149.                     ++dp;
  150.                     ++cp;
  151.                     b >>= 1;
  152.                     if (b == 0) {
  153.                         b = 0x80;
  154.                         ++sp;
  155.                     }
  156.                 }
  157.                 ReleaseResource(h);
  158.             }
  159.         }
  160.     }
  161.     DrawStatusBar(NbrSprites,NbrSprites+1);
  162.  
  163.     // Load Top Scores
  164.     LoadTopScores();
  165.     DrawStatusBar(NbrSprites+1,NbrSprites+1);
  166.     
  167.     SetPort(theWin);
  168. }
  169.  
  170. // CleanUp before quitting
  171. //
  172. void CleanUp(void)
  173. {
  174.     EndSounds();
  175.     ShowCursor();
  176.     MBarHeight = oldMBarHeight;
  177.     CloseResFile(gResFile);
  178. }
  179.  
  180. void DrawStatusBar(short curLevel, short maxLevel)
  181. {
  182.     CGrafPtr        curPort;
  183.     GDHandle        curDevice;
  184.     Rect            r,r2;
  185.     RGBColor        saveColor,foreColor;
  186.  
  187.     GetGWorld(&curPort,&curDevice);
  188.     SetGWorld(gOffScreen,NULL);
  189.  
  190.     SetRect(&r,0,0,412,2);
  191.  
  192.     // Position within 512x384 Rectangle
  193.     OffsetRect(&r,(512-412)/2,384-8);
  194.  
  195.     // Position within offscreen video area (which is scaled to monitor size)
  196.     // But max 640 x 480
  197.     OffsetRect(&r,(gOffScreen->portRect.right - 512)/2,
  198.                   (gOffScreen->portRect.bottom - 384)/2);
  199.  
  200.     GetForeColor(&saveColor);
  201.     foreColor.red = 0x0000;
  202.     foreColor.green = 0x9999;
  203.     foreColor.blue = 0xCCCC;
  204.     RGBForeColor(&foreColor);
  205.     PaintRect(&r);
  206.     r2 = r;
  207.     r2.right -= (r2.right-r2.left)-(((r2.right-r2.left)*curLevel)/maxLevel);
  208.     foreColor.green = 0;
  209.     RGBForeColor(&foreColor);
  210.     PaintRect(&r2);
  211.     RGBForeColor(&saveColor);
  212.  
  213.     SetGWorld(curPort,curDevice);
  214.  
  215.     MyCopyRect(&r);
  216. }
  217.  
  218. // Display Attract Mode Screen
  219. void BeginAttract(void)
  220. {
  221.     CGrafPtr    curPort;
  222.     GDHandle    curDevice;
  223.     static         StringPtr promptStr1 = "\pHit P to Play";
  224.     static         StringPtr promptStr2 = "\p0-7 to change volume";
  225.     static         StringPtr promptStr3 = "\pK to redefine keys";
  226.  
  227.  
  228.     GetGWorld(&curPort,&curDevice);
  229.  
  230.     SetGWorld(gOffScreen,NULL);
  231.     PaintRect(&gOffScreen->portRect);
  232.  
  233.     DisplayPicture(BackgroundPICT,-1,-1);
  234.     if (g12InchMode)
  235.         DisplayPicture(SmallLogoPICT,42,42);
  236.     else
  237.         DisplayPicture(SmallLogoPICT,64,64);
  238.  
  239.     DisplayTopScores();
  240.  
  241.     TextFont(geneva);
  242.     TextSize(12);
  243.     TextFace(bold);
  244.     TextMode(srcBic);
  245.  
  246.     CenterString(promptStr1);    Move(0,g12InchMode? -16 : -32);
  247.     DrawString(promptStr1);
  248.  
  249.     CenterString(promptStr2);    Move(0,0);
  250.     DrawString(promptStr2);
  251.  
  252.     CenterString(promptStr3);    Move(0,g12InchMode? 16 : 32);
  253.     DrawString(promptStr3);
  254.  
  255.     TextMode(srcCopy);
  256.  
  257.  
  258.     SetGWorld(curPort,curDevice);
  259.  
  260.     GammaFadeOut();
  261.     MyCopyBits();
  262.     GammaFadeIn();
  263. }
  264.  
  265.  
  266. void BeginGame(void)
  267. {
  268.     short    i;
  269.     CGrafPtr        curPort;
  270.     GDHandle        curDevice;
  271.     short            eMask;
  272.  
  273.     register        SpriteInstance    *sp;
  274.  
  275.     gMaxSprite = 0;
  276.     gNbrSaveMaps = 0;
  277.     gSpriteCnt = 0;
  278.     gSparkCnt = 0;
  279.     gAsteroidCnt = 0;
  280.     gRemainingShips = 3;
  281.     gGameLevel = 0;
  282.     gGameScore = 0L;
  283.     gShieldPower = MaxShieldPower;
  284.  
  285.     GetGWorld(&curPort,&curDevice);
  286.  
  287.     SetGWorld(gOffScreen,NULL);
  288.  
  289.     TextFont(geneva);
  290.     TextSize(9);
  291.     TextFace(0);
  292.     TextMode(notSrcCopy);
  293.     PaintRect(&gOffScreen->portRect);
  294.  
  295.     DisplayPicture(BackgroundPICT,-1,-1);
  296.  
  297.     SetGWorld(curPort,curDevice);
  298.  
  299.     InitStatusDisplay();    // Display sprites should be at the lowest level
  300.  
  301.     // NewDebugDisplay();        // Debug Sprites Next
  302.  
  303.     InitLevel();            // Asteroids
  304.  
  305.     NewShip();                // Ship
  306.  
  307.     GammaFadeOut();
  308.     MyCopyBits();
  309.     GammaFadeIn();
  310.  
  311.     // Draw New Sprites
  312.     for (i = 0,sp = sTable; i < gMaxSprite; ++i,++sp)
  313.         (*sDef[sp->type].drawFunc)(sp);
  314.  
  315.     eMask = everyEvent;
  316.     eMask &= ~keyUpMask;
  317.     eMask &= ~keyDownMask;
  318.     eMask &= ~autoKeyMask;
  319.     SetEventMask(eMask);
  320.  
  321.     gGameState = GS_Play;
  322. }
  323.  
  324. void EndGame(void)
  325. {
  326.     short            eMask;
  327.     CGrafPtr        curPort;
  328.     GDHandle        curDevice;
  329.     static StringPtr promptStr = "\pGame Over";
  330.     SpriteInstance    *sp;
  331.     short            i;
  332.     long            t;
  333.     EventRecord        dmyEvent;
  334.  
  335.     i = gMaxSprite;
  336.     sp = &sTable[i-1];
  337.     while (i--) {
  338.         if (sp->active)
  339.             (*sDef[sp->type].eraseFunc)(sp);
  340.         --sp;
  341.     }
  342.  
  343.     GetGWorld(&curPort,&curDevice);
  344.  
  345.     SetGWorld(gOffScreen,NULL);
  346.  
  347.     TextFont(geneva);
  348.     TextSize(12);
  349.     TextFace(bold);
  350.     TextMode(srcBic);
  351.     CenterString(promptStr);
  352.     DrawString(promptStr);
  353.  
  354.     SetGWorld(curPort,curDevice);
  355.     MyCopyBits();
  356.  
  357.  
  358.     eMask = everyEvent;
  359.     eMask &= ~keyUpMask;
  360.     SetEventMask(eMask);
  361.     FlushEvents(everyEvent, 0);
  362.  
  363.     t = TickCount();
  364.  
  365.     IntegrateScore(gGameScore, gGameLevel);
  366.  
  367.     gGameState = GS_Attract;
  368.     while (TickCount() - t < 180L &&
  369.             !OSEventAvail(everyEvent, &dmyEvent));
  370.             ;
  371. }
  372.  
  373. // Create a new batch of asteroids
  374. void InitLevel(void)
  375. {
  376.     short    i,n;
  377.  
  378.     n = gGameLevel/2 + 3;
  379.     if (n > 20)
  380.         n = 20;
  381.  
  382.     while (n--)
  383.         NewAsteroid((n+gGameLevel) % NbrAsteroids);
  384.  
  385.     gScoreMultiply = 1;
  386.  
  387.     gBadGuyChance = 1200 - 100*(gGameLevel/3);
  388.  
  389.     if (gBadGuyChance < 300)
  390.         gBadGuyChance = 300;
  391.  
  392.     gYummyChance = 1000;
  393. }
  394.  
  395. // Level was completed successfully
  396. void EndLevel(void)
  397. {
  398.     ++gGameLevel;
  399.     AddScore(LevelBonusScore*gGameLevel);
  400.     PlaySound(S_LevelCompletion, 4);
  401. }
  402.  
  403. // Main Game Loop
  404. //
  405. void MainGameLoop(WindowPtr theWin)
  406. {
  407.     register SpriteInstance    *sp;
  408.     register short    i,n;
  409.     register long    t;
  410.     static EventRecord    dmyEvent;
  411.     Rect    r;
  412. #if DEBUGGING
  413.     void    IdleTest();                // Debugging Function used to determine CPU load for profiler
  414. #endif
  415.  
  416. #if __option(profile)            // 6/15 Optional profiling support
  417.     _profile = 1;
  418. #endif
  419.  
  420.     SetPort(theWin);
  421.  
  422.     do {
  423.         t = Ticks;
  424.     
  425.         // If we're ready for next frame of animation
  426.         //
  427.         if (t - gLastDispTime >= gGameClockTicks) {
  428.             //  Update the game clock
  429.             ++gGameClock;
  430.             gLastDispTime = t;
  431.     
  432.             // Compact Sprite Table if necessary
  433.             if (gMaxSprite > 20 && gMaxSprite > (gSpriteCnt<<1)) {
  434.                 for (i = n = 0; i < gMaxSprite; ++i) {
  435.                     if (sTable[i].active) {
  436.                         sTable[n] = sTable[i];
  437.                         if (sTable[n].saveMapPtr)
  438.                             sTable[n].saveMapPtr->sp = &sTable[n];
  439.                         if (gShip == &sTable[i])
  440.                             gShip = &sTable[n];
  441.                         ++n;
  442.                     }
  443.                 }
  444.                 gMaxSprite = n;
  445.                 for (i = n = 0; i < gNbrSaveMaps; ++i) {
  446.                     if (smTable[i].active) {
  447.                         smTable[n] = smTable[i];
  448.                         smTable[n].sp->saveMapPtr = &smTable[n];
  449.                         ++n;
  450.                     }
  451.                 }
  452.                 gNbrSaveMaps = n;
  453.             }
  454.     
  455.  
  456.             // Erase all Sprites from offscreen gworld
  457.             // Each sprite has a pointer to an erase function
  458.             //
  459.             i = gMaxSprite;
  460.             sp = &sTable[i-1];
  461.             while (i--) {
  462.                 if (sp->active)
  463.                     (*sDef[sp->type].eraseFunc)(sp);
  464.                 --sp;
  465.             }
  466.     
  467.  
  468.             // Advance to next play level if necessary
  469.             if (gAsteroidCnt == 0) {
  470.                 EndLevel();
  471.                 InitLevel();
  472.             }
  473.  
  474.             // Check Keyboard - respond to keys
  475.             CheckKeys();
  476.  
  477.             // Random Events - bad guys, yummies
  478.             if (MyRandom(gBadGuyChance) == 0) {    // 1200 Random Bad Guys
  479.                 if (MyRandom(30) == 0)            // Every 30 minutes
  480.                     n = MyRandom(7);            // Lucky 7!
  481.                 else
  482.                     n = 1;
  483.                 while (n--) {
  484.                     switch (MyRandom(5)) {
  485.                     case 0:
  486.                     case 1:
  487.                         NewSaucer();
  488.                         break;
  489.                     case 2:
  490.                     case 3:
  491.                         NewBarbell();
  492.                         break;
  493.                     case 4:
  494.                         NewCube();
  495.                         break;
  496.                         
  497.                     }
  498.                 }
  499.             }
  500.  
  501.             // Random Yummies
  502.             if (gYummyCnt == 0 && MyRandom(gYummyChance) == 0)    // Random Yummys
  503.                 NewYummy();
  504.  
  505.             // Move Sprites - using Sprite's moveFunction
  506.             //
  507.             n = gMaxSprite;
  508.             for (i = 0,sp=sTable; i < n; ++i,++sp) {
  509.                 // Update Sprite Position
  510.                 if (sp->active)
  511.                     (*sDef[sp->type].moveFunc)(sp);
  512.             }
  513.  
  514.             // Draw Sprites in Offscreen GWorld
  515.             //
  516.             n = gMaxSprite;
  517.             for (i = 0,sp=sTable; i < n; ++i,++sp) {
  518.                 // Render Sprite in New Position
  519.                 if (sp->active)
  520.                     (*sDef[sp->type].drawFunc)(sp);
  521.             }
  522.     
  523.             // Render Sprites onto Onscreen video
  524.             //
  525.             n = gMaxSprite;
  526.             for (i = 0,sp=sTable; i < n; ++i,++sp) {
  527.                 // Render Sprite in New Position, if it needs updating
  528.                 //
  529.                 if (sp->update) {
  530.                     register Point    p1,p2;
  531.  
  532.                     sp->update = false;
  533.  
  534.                     p1 = sp->oldPos;
  535.                     p2 = sp->pos;
  536.                     // If distance between old and new positions is large
  537.                     // than sprite has crossed boundaries from one side of screen
  538.                     // to the other, in this case the part of the sprite that was
  539.                     // erased is on the other side of the screen and needs to
  540.                     // be drawn separately
  541.                     //
  542.                     if (abs(p2.h - p1.h) > 320 ||
  543.                         abs(p2.v - p1.v) > 240)
  544.                     {
  545.                         // Object has crossed boundaries, draw two separate rects
  546.                         r.left = p1.h;
  547.                         r.top =  p1.v;
  548.                         r.right = p1.h+sp->width;
  549.                         r.bottom = p1.v+sp->width;
  550.                         // Clip
  551.                         if (r.left < IconWidth)
  552.                             r.left = IconWidth;
  553.                         if (r.top < IconHeight)
  554.                             r.top = IconHeight;
  555.                         if (r.right >= gPlayRect.right)
  556.                             r.right = gPlayRect.right-1;
  557.                         if (r.bottom >= gPlayRect.bottom)
  558.                             r.bottom = gPlayRect.bottom-1;
  559.                         if (r.right > r.left && r.bottom > r.top)
  560.                             MyCopyRect(&r);
  561.                         r.left = p2.h;
  562.                         r.top =  p2.v;
  563.                         r.right = p2.h+sp->width;
  564.                         r.bottom = p2.v+sp->width;
  565.                         // Clip
  566.                         if (r.left < IconWidth)
  567.                             r.left = IconWidth;
  568.                         if (r.top < IconHeight)
  569.                             r.top = IconHeight;
  570.                         if (r.right >= gPlayRect.right)
  571.                             r.right = gPlayRect.right-1;
  572.                         if (r.bottom >= gPlayRect.bottom)
  573.                             r.bottom = gPlayRect.bottom-1;
  574.                         if (r.right > r.left && r.bottom > r.top)
  575.                             MyCopyRect(&r);
  576.                     }
  577.                     // Otherwise, the part that was erased is nearby
  578.                     // we merge those two rects into one rect and
  579.                     // copy the whole area in one pass
  580.                     //
  581.                     else {
  582.                         // Merge rects into composite rect
  583.                         r.left = (p1.h < p2.h)? p1.h : p2.h;
  584.                         r.top = (p1.v < p2.v)? p1.v : p2.v;
  585.                         r.right = (p1.h > p2.h)? p1.h : p2.h;
  586.                         r.right += sp->width;
  587.                         r.bottom = (p1.v > p2.v)? p1.v : p2.v;
  588.                         r.bottom += sp->width;
  589.                         // Clip
  590.                         if (r.left < IconWidth)
  591.                             r.left = IconWidth;
  592.                         if (r.top < IconHeight)
  593.                             r.top = IconHeight;
  594.                         if (r.right >= gPlayRect.right)
  595.                             r.right = gPlayRect.right-1;
  596.                         if (r.bottom >= gPlayRect.bottom)
  597.                             r.bottom = gPlayRect.bottom-1;
  598.                         if (r.right > r.left && r.bottom > r.top)
  599.                             MyCopyRect(&r);
  600.                     }
  601.                     sp->oldPos = sp->pos;
  602.                 }
  603.             }
  604.         }
  605. #if DEBUGGING
  606.         // This debugging function is used with the profiler to determine
  607.         // how much idle time I am getting on a slow machine
  608.         // this was used to determine how fast I could make the game clock
  609.         // it is currently set at 20 frames a second
  610.         else {
  611.             IdleTest();
  612.         }
  613. #endif
  614.     // Keep looping as long as game is in play
  615.     // If game is over, keep looping while there are visible sparks from the
  616.     // last explosion
  617.     } while (gGameState == GS_Play || (gGameState == GS_GameOver && gSparkCnt));
  618. #if __option(profile)            // 6/15 Optional profiling support
  619.     _profile = 0;
  620. #endif
  621.     FlushEvents(everyEvent,0);
  622. }
  623.  
  624. void InitStatusDisplay(void)
  625. {
  626.     register SpriteInstance *sp;
  627.     if ((sp = NewSprite(false)) == NULL)
  628.         return;
  629.     sp->type = ST_StatusDisplay;
  630.     sp->param1 = 0;
  631.     sp->pos.h = 40;
  632.     sp->pos.v = 64;
  633.     sp->width = 2;
  634.     gLastScore = -1;
  635.     gLastRemainingShips = -1;
  636.     gLastLevel = -1;
  637.     gLastScoreMultiply = -1;
  638.     gLastShieldPower = -1;
  639. }
  640.  
  641. // Two different sets of X coordinates are used for the status bar
  642. // elements depending on whether I'm on a 12 inch monitor or not
  643.  
  644. enum {XScore, XLevel, XPots, XMultiply, XPause, XShields};
  645.  
  646. static short    xP[2][6] = {{40,122,202,286,352,520},
  647.                             {40,122-32,202-32,286-32,352-32,520-128}};
  648.  
  649. // Draw the game status bar on top of the screen
  650. //
  651. void StatusDraw(register SpriteInstance *sp)
  652. {
  653.     static char        scoreText[32];
  654.     Rect            r;
  655.     register short    cw;
  656.  
  657.     if (gLastScore != gGameScore) {
  658.         gLastScore = gGameScore;
  659.         StatusPrintf(xP[g12InchMode][XScore],44,"%-10ld",gGameScore);
  660.     }
  661.     if (gLastLevel != gGameLevel) {
  662.         gLastLevel = gGameLevel;
  663.         StatusPrintf(xP[g12InchMode][XLevel],44,"Level: %d", gGameLevel);
  664.     }
  665.     if (gLastRemainingShips != gRemainingShips) {
  666.         gLastRemainingShips = gRemainingShips;
  667.         StatusPrintf(xP[g12InchMode][XPots],44,"Pots: %d", gRemainingShips);
  668.     }
  669.     if (gLastScoreMultiply != gScoreMultiply) {
  670.         gLastScoreMultiply = gScoreMultiply;
  671.         if (gScoreMultiply > 1)
  672.             StatusPrintf(xP[g12InchMode][XMultiply], 44,"x%d     ", gScoreMultiply);
  673.         else
  674.             StatusPrintf(xP[g12InchMode][XMultiply], 44,"        ", gScoreMultiply);
  675.     }
  676.     if (gLastShieldPower != gShieldPower) {
  677.         RGBColor    fc;
  678.         Rect        r;
  679.         CGrafPtr        curPort;
  680.         GDHandle        curDevice;
  681.  
  682.         GetGWorld(&curPort,&curDevice);
  683.         SetGWorld(gOffScreen,NULL);
  684.  
  685.         gLastShieldPower = gShieldPower;
  686.         MoveTo(xP[g12InchMode][XShields],40);
  687.  
  688.         fc.red = 0x0000;    fc.green = fc.blue = 0x8080;
  689.         RGBForeColor(&fc);
  690.         Line(gShieldPower,0);
  691.  
  692.         fc.red = 0xFFFF;    fc.green = fc.blue = 0x0000;
  693.         RGBForeColor(&fc);
  694.         Line(MaxShieldPower-gShieldPower,0);
  695.  
  696.         fc.red = fc.green = fc.blue = 0x0000;
  697.         RGBForeColor(&fc);
  698.         SetRect(&r,xP[g12InchMode][XShields],40,xP[g12InchMode][XShields]+100,41);
  699.         MyCopyRect(&r);
  700.  
  701.         SetGWorld(curPort,curDevice);
  702.     }
  703. }
  704.  
  705. // Keep track of game score
  706. //
  707. void AddScore(short amt)
  708. {
  709.     gGameScore += amt * (long) gScoreMultiply;
  710.     if (gGameScore/50000L != gLastScore/50000) {
  711.         ++gRemainingShips;
  712.         PlaySound(S_ExtraShip, 4);
  713.     }
  714. }
  715.  
  716. // Print part of top score display
  717. //
  718. void PrintfXY(short x, short y, char *template, ...)
  719. {
  720.     CGrafPtr        curPort;
  721.     GDHandle        curDevice;
  722.     char             tbuf[128];
  723.     va_list         args;
  724.     Rect            r;
  725.     register        short cw;
  726.  
  727.     va_start(args,template);
  728.     vsprintf(tbuf,template,args);
  729.     va_end(args);
  730.  
  731.     CtoPstr(tbuf);
  732.  
  733.     cw = StringWidth((StringPtr) tbuf);
  734.     MoveTo(x,y);
  735.     DrawString((StringPtr) tbuf);
  736. }
  737.  
  738. // Move to the right position to center a string
  739. //
  740. void CenterString(StringPtr str)
  741. {
  742.     MoveTo(((gOffscreenRect.right - gOffscreenRect.left)-StringWidth(str))/2,
  743.             (gOffscreenRect.bottom - gOffscreenRect.top)/2);
  744. }
  745.  
  746. // Print part of status bar display
  747. //
  748. void StatusPrintf(short x, short y, char *template, ...)
  749. {
  750.     CGrafPtr        curPort;
  751.     GDHandle        curDevice;
  752.     char             tbuf[128];
  753.     va_list         args;
  754.     Rect            r;
  755.     register        short cw;
  756.  
  757.     va_start(args,template);
  758.     vsprintf(tbuf,template,args);
  759.     va_end(args);
  760.  
  761.     CtoPstr(tbuf);
  762.  
  763.     GetGWorld(&curPort,&curDevice);
  764.     SetGWorld(gOffScreen,NULL);
  765.     cw = StringWidth((StringPtr) tbuf);
  766.     MoveTo(x,y);
  767.     DrawString((StringPtr) tbuf);
  768.     SetGWorld(curPort,curDevice);
  769.  
  770.     r.left = x;
  771.     r.top = y - 10;
  772.     r.right = x + cw;
  773.     r.bottom = y;
  774.  
  775.     MyCopyRect(&r);
  776. }
  777.  
  778. // Keyboard handling
  779. // for speed, we use GetKeys function which polls entire keyboard
  780. // bypassing normal WaitNextEvent handlng
  781. //
  782. // DeBounce is used to prevent rapid firing
  783. // in "Uzi" mode, debounce is less to allow rapid firing
  784. //
  785. #define Debounce        10
  786. #define UziDebounce        1
  787.  
  788. short    fireCtr=0;
  789.  
  790. void CheckKeys(void)
  791. {
  792.     static KeyMap    km;
  793.     register short    flags;
  794.     extern SpriteInstance    *gShip;
  795.  
  796.     GetKeys(km);
  797.  
  798.  
  799.     // Non Ship Related Stuff
  800.     //
  801.     if ((((char *) km)[1] & 0x10) ||        // 0x0C Q
  802.         (((char *) km)[6] & 0x20)) {        // 0x35 ESC
  803.         gGameState = GS_GameOver;
  804.     }
  805.  
  806. #if DEBUGGING
  807.     if (((char *) km)[5] & 0x04) {            // 0x2A \        trigger random event
  808.         switch (MyRandom(4)) {
  809.         case 0:    NewYummy();        break;
  810.         case 1:    NewSaucer();     break;
  811.         case 2: NewBarbell();    break;
  812.         case 3: NewCube();        break;
  813.         }
  814.     }
  815.     if (((char *) km)[6] & 0x04) {            // 0x32 ~        trigger random event
  816.         switch (MyRandom(2)) {
  817.         case 0:    NewYummy();                break;
  818.         case 1: NewAsteroid(ST_Jim);    break;
  819.         }
  820.     }
  821. #endif
  822.  
  823.     if (((char *) km)[4] & 0x08) {            // 0x23 P
  824.         do {
  825.             GetKeys(km);
  826.         } while (((char *) km)[4] & 0x08);    // Wait for Key Up
  827.  
  828.         // Print Pause Message
  829.         StatusPrintf(xP[g12InchMode][XPause],44,"PAUSE");
  830.  
  831.         do {
  832.             GetKeys(km);
  833.         } while (!(((char *) km)[4] & 0x08));    // Wait for Key Down
  834.         do {
  835.             GetKeys(km);
  836.         } while (((char *) km)[4] & 0x08);    // Wait for Key Up
  837.  
  838.         // Print UnPause Message
  839.         StatusPrintf(xP[g12InchMode][XPause],44,"             ");
  840.     }
  841.  
  842.     // Ship Related Stuff
  843.     //
  844.  
  845.     if (!gShip)
  846.         return;
  847.  
  848.     flags = false;
  849.  
  850.     if (((char *) km)[gPrefs.thrustKeyByte] & gPrefs.thrustKeyBit) {
  851.         flags |= SF_Thrust;
  852.     }
  853.  
  854.     if (((char *) km)[gPrefs.fireKeyByte] & gPrefs.fireKeyBit) {
  855.         if (fireCtr == 0) {
  856.             flags |= SF_Fire;
  857.             fireCtr = ((gShipMode & SM_Uzi)? UziDebounce : Debounce);
  858.         }
  859.         else
  860.             --fireCtr;
  861.     }
  862.     else
  863.         fireCtr = 0;
  864.  
  865.     if (((char *) km)[gPrefs.leftKeyByte] & gPrefs.leftKeyBit) {
  866.         flags |= SF_Left;
  867.     }
  868.  
  869.     if (((char *) km)[gPrefs.rightKeyByte] & gPrefs.rightKeyBit) {
  870.         flags |= SF_Right;
  871.     }
  872.  
  873.     if (((char *) km)[gPrefs.shieldKeyByte] & gPrefs.shieldKeyBit) {
  874.         if (gShieldPower > 0) {
  875.             gShieldPower--;                // Use up shield Power
  876.             flags |= SF_Shield;
  877.         }
  878.     }
  879.     else
  880.         if ((gGameClock & 0xFF) == 0)        // Increase Shield Power every 16 secs
  881.             if (gShieldPower < MaxShieldPower)
  882.                 gShieldPower += 5;
  883.  
  884.     if (gShipMode & SM_AutoShield) {
  885.         if (gShip && gShip->tickCtr > 40)
  886.             gShipMode &= ~SM_AutoShield;
  887.         else
  888.             flags |= SF_Shield;
  889.     }
  890.  
  891.     gShip->param1 = flags;
  892.  
  893. }
  894.  
  895. #if DEBUGGING
  896. void IdleTest()
  897. {
  898.     short    x;
  899.     x = 0;
  900. }
  901. #endif
  902.